package model;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.mybeans.dao.DAOException;
import org.mybeans.factory.BeanFactory;
import org.mybeans.factory.BeanFactoryException;
import org.mybeans.factory.BeanTable;
import org.mybeans.factory.DuplicateKeyException;
import org.mybeans.factory.MatchArg;
import org.mybeans.factory.RollbackException;
import org.mybeans.factory.Transaction;

import databeans.EntityAttribute;
import interfas.EntityAttributeInterface;

public class EntityAttributeDAO implements EntityAttributeInterface {
	private List<Connection> connectionPool = new ArrayList<Connection>();
	private String jdbcDriver;
	private String jdbcURL;
	private BeanFactory<EntityAttribute> factory;
	
	public EntityAttributeDAO(String jdbcDriver, String jdbcURL) throws DAOException {
		try {
			this.jdbcDriver = jdbcDriver;
			this.jdbcURL = jdbcURL;
			BeanTable<EntityAttribute> table = BeanTable.getInstance(EntityAttribute.class, "entityattribute");
			if (!table.exists())
				table.create("eid", "attrname");
			
			table.setIdleConnectionCleanup(true);
			factory = table.getFactory();
		} catch (BeanFactoryException e) {
			throw new DAOException(e);
		}
	}
	
	private synchronized Connection getConnection() throws DAOException {
		if (connectionPool.size() > 0) {
			return connectionPool.remove(connectionPool.size() - 1);
		}
		try {
			Class.forName(jdbcDriver);
		} catch (ClassNotFoundException e){
			throw new DAOException(e);
		}
		try {
			return DriverManager.getConnection(jdbcURL);
		} catch (SQLException e) {
			throw new DAOException(e);
		}
	}
	
	private synchronized void releaseConnection(Connection con) {
		connectionPool.add(con);
	}
	
	public void create(EntityAttribute ea, EntityDAO entityDAO) throws DAOException {
		try {
			// Check for foreign key constraint
			if (entityDAO.lookup(ea.getEid()) == null) {
				throw new DAOException("eid must exist in entity table");
			}
			
			Transaction.begin();
			EntityAttribute dbea = factory.create(ea.getEid(), ea.getAttrname());
			factory.copyInto(ea, dbea);
			Transaction.commit();
		} catch (DuplicateKeyException e) {
			throw new DAOException("An entityattribute with eid " + ea.getEid() + " and attrname " + ea.getAttrname() + " already exists");
		} catch (RollbackException e) {
			throw new DAOException(e);
		} finally {
			if (Transaction.isActive())
				Transaction.rollback();
		}
	}

	public EntityAttribute lookup(int eid, String attrname) throws DAOException {
		try {
			return factory.lookup(eid, attrname);
		} catch (RollbackException e) {
			throw new DAOException(e);
		}
	}
	
	public EntityAttribute[] getAttrByEid(int eid) throws DAOException {
		try {
			return factory.match(MatchArg.equals("eid", eid));
		} catch (RollbackException e) {
			throw new DAOException(e);
		}
	}
	
	public String[] getAllAttributesByEcid(int ecid) throws DAOException {
		Connection con = null;
		try {
			con = getConnection();
			con.setAutoCommit(false);
			
			Statement stmt = con.createStatement();
			ResultSet rs = stmt.executeQuery("SELECT DISTINCT attrname FROM entityattribute " +
					"INNER JOIN entity ON entityattribute.eid=entity.eid " +
					"INNER JOIN entityclass ON entity.ecid=entityclass.ecid " +
					"WHERE entityclass.ecid=" + ecid);
			List<String> list = new ArrayList<String>();
			while (rs.next()) {
				String attr = rs.getString("attrname");
				list.add(attr);
			}
			stmt.close();
			releaseConnection(con);
			
			return list.toArray(new String[list.size()]);
		} catch (SQLException e) {
			try {
				if (con != null)
					con.close();
			} catch (SQLException e2) {}
			throw new DAOException(e);
		}
	}
	
	public void delete(int eid, String attrname) throws DAOException {
		try {
			Transaction.begin();
			EntityAttribute dbea = lookup(eid, attrname);
			if (dbea == null)
				throw new DAOException("EntityAttribute with eid " + eid + " and attrname " + attrname + " doesn't exist");
			factory.delete(eid, attrname);
			Transaction.commit();
		} catch (RollbackException e) {
			throw new DAOException(e);
		} finally {
			if (Transaction.isActive())
				Transaction.rollback();
		}
	}
	
	public int size() throws DAOException {
		try {
			return factory.getBeanCount();
		} catch (RollbackException e) {
			throw new DAOException(e);
		}
	}
}
